package com.zqweb.gulimall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.zqweb.common.constant.OrderConstant;
import com.zqweb.common.exception.SkuWareException;
import com.zqweb.common.to.SeckillOrderTo;
import com.zqweb.common.to.SkuStockTo;
import com.zqweb.common.utils.R;
import com.zqweb.common.vo.MemberUserInfoVo;
import com.zqweb.gulimall.order.config.MyThreadPoolConfig;
import com.zqweb.gulimall.order.entity.OrderItemEntity;
import com.zqweb.gulimall.order.entity.PaymentInfoEntity;
import com.zqweb.gulimall.order.enums.OrderStatusEnum;
import com.zqweb.gulimall.order.feign.CartFeignService;
import com.zqweb.gulimall.order.feign.MemberFeignService;
import com.zqweb.gulimall.order.feign.ProductFeignService;
import com.zqweb.gulimall.order.feign.WareFeignService;
import com.zqweb.gulimall.order.interceptor.OrderLoginInterceptor;
import com.zqweb.gulimall.order.service.OrderItemService;
import com.zqweb.gulimall.order.service.PaymentInfoService;
import com.zqweb.gulimall.order.to.OrderCreateTo;
import com.zqweb.gulimall.order.to.OrderTo;
import com.zqweb.gulimall.order.vo.*;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zqweb.common.utils.PageUtils;
import com.zqweb.common.utils.Query;

import com.zqweb.gulimall.order.dao.OrderDao;
import com.zqweb.gulimall.order.entity.OrderEntity;
import com.zqweb.gulimall.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
    @Autowired
    private MemberFeignService memberFeignService;
    @Autowired
    private CartFeignService cartFeignService;
    @Autowired
    private WareFeignService wareFeignService;
    @Autowired
    private ProductFeignService productFeignService;
    @Autowired
    private ThreadPoolExecutor threadPool;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private OrderItemService orderItemService;
    @Autowired
    private PaymentInfoService paymentInfoService;
    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo getOrderConfirmVo() throws ExecutionException, InterruptedException {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        MemberUserInfoVo memberUserInfoVo = OrderLoginInterceptor.threadLocal.get();
        /*
         将老请求的RequestAttributes设置到RequestContextHolder上下文中，防止采用CompletableFuture异步编排方式时
         开辟新线程后拿不到原来线程中存放的老请求信息（ThreadLocal机制）
         即，对每个新Thread，重新设置下ThreadLocal，填充好之前的老请求RequestAttributes
          */
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        CompletableFuture<Void> getMemberAddressVosTask = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            // 1、获取并设置当前登录用户的收获地址列表
            List<MemberAddressVo> memberAddressVos = memberFeignService.getMemberAddressListById(memberUserInfoVo.getId());
            orderConfirmVo.setMemberAddressVos(memberAddressVos);
        }, threadPool);
        CompletableFuture<Void> getOrderItemVosTask = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            // 2、 获取并设置当前登录用户的购物车中所有选中的要购买的购物项商品
            List<OrderItemVo> orderItemVos = cartFeignService.getUserCartItems();
            orderConfirmVo.setItems(orderItemVos);
        }, threadPool).thenRunAsync(() -> {
            // 针对每个购物项商品，再远程调用ware库存服务，查询每个购物项商品的是否有库存状态，并设置到orderConfirmVo中
            List<OrderItemVo> items = orderConfirmVo.getItems();
            if(items != null && items.size() > 0){
                List<Long> skuIds = items.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
                Long[] skuIdArray = skuIds.toArray(new Long[skuIds.size()]);
                R r = wareFeignService.hasStock(skuIdArray);
                if(r != null && r.getCode() == 0){
                    List<SkuStockTo> skuStockTos = r.getData("data", new TypeReference<List<SkuStockTo>>() {
                    });
                    if(skuStockTos != null && skuStockTos.size() > 0){
                        Map<Long, Boolean> stocks = skuStockTos.stream().collect(Collectors.toMap(SkuStockTo::getSkuId, SkuStockTo::getHasStock));
                        orderConfirmVo.setStocks(stocks);
                    }
                }
            }
        },threadPool);

        // 3、获取并设置用户积分
        orderConfirmVo.setIntegration(memberUserInfoVo.getIntegration());
        // 4、其他数据信息自行计算
        // 5、设置防重令牌（幂等性处理）
        String orderKey = OrderConstant.ORDER_TOKEN_PREFIX+memberUserInfoVo.getId();
        String orderToken = UUID.randomUUID().toString().replace("-","");
        stringRedisTemplate.opsForValue().set(orderKey,orderToken,30, TimeUnit.MINUTES);
        orderConfirmVo.setOrderToken(orderToken);

        CompletableFuture.allOf(getMemberAddressVosTask,getOrderItemVosTask).get();
        System.out.println("获取到的OrderConfirmVo为：\n" + orderConfirmVo);
        return orderConfirmVo;
    }

    @Override
//    @GlobalTransactional // 在分布式事务主业务方法入口处，标注该全局事务注解
    @Transactional // 标识为本地事务，也要标注
    public OrderSubmitRespVo orderSubmitVo(OrderSubmitVo orderSubmitVo) {
//        System.out.println("进入orderSubmitVo方法...");
        OrderSubmitRespVo orderSubmitRespVo = new OrderSubmitRespVo();
        // 1、验证order的token令牌（若相同还要将token从Redis中删除，而且保证验证+删除是原子性操作）
        String orderToken = orderSubmitVo.getOrderToken();
        String redisRuaScript = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        Long userId = OrderLoginInterceptor.threadLocal.get().getId();
        // Redis的Rua脚本默认返回为Long类型
        Long execute = stringRedisTemplate.execute(new DefaultRedisScript<>(redisRuaScript, Long.class), Arrays.asList(OrderConstant.ORDER_TOKEN_PREFIX + userId), orderToken);
        System.out.println("execute = " + execute);
        if(execute == 0L){
            // orderToken验证失败
            orderSubmitRespVo.setHandleStatus(0);
            return orderSubmitRespVo;
        }else{
            // 验证成功，继续其他的数据构造和处理
            OrderCreateTo orderCreateTo = createOrderTo(orderSubmitVo);
            System.out.println("orderCreateTo = " + orderCreateTo);
            // 3、验价
            BigDecimal diff = orderCreateTo.getOrder().getPayAmount().subtract(orderSubmitVo.getPayPrice());
            if(Math.abs(diff.doubleValue()) < 0.01){
        // 前台提交的应付价格和后台计算得到的应付价格之差小于0.01，才继续处理，否则打回确认页，重新确认并提交订单数据
                orderSubmitRespVo.setHandleStatus(2);// 验价失败
                return orderSubmitRespVo;
            }else{
                // 继续后续的业务处理
                // 4、保存订单相关数据（订单 + 订单项）
                this.saveOrderInfo(orderCreateTo);

                // 5、锁定库存
                // 先构造指定订单需要锁定的库存vo
                WareLockVo wareLockVo = new WareLockVo();
                wareLockVo.setOrderSn(orderCreateTo.getOrder().getOrderSn());
                List<OrderItemVo> orderItemVos = orderCreateTo.getOrItems().stream().map(item -> {
                    OrderItemVo orderItemVo = new OrderItemVo();
                    orderItemVo.setSkuId(item.getSkuId());
                    orderItemVo.setCount(item.getSkuQuantity());
                    orderItemVo.setTitle(item.getSkuName());

                    return orderItemVo;
                }).collect(Collectors.toList());
                wareLockVo.setLocks(orderItemVos);
                // 远程调用ware服务的锁定库存接口
                R r = wareFeignService.lockOrder(wareLockVo);
                if(r != null && r.getCode() == 0){
                    // 库存锁定成功
                    orderSubmitRespVo.setHandleStatus(1);
                    orderSubmitRespVo.setOrderEntity(orderCreateTo.getOrder());

                    // TODO 6、远程扣减用户积分

                    // 创建订单成功以后，发送消息给MQ的order.delay.queue队列
                    rabbitTemplate.convertAndSend("order-event-exchange","order.create.order",orderCreateTo.getOrder());

                    return orderSubmitRespVo;
                }else{
                    // 库存锁定失败（只要发生异常，就会触发事务的回滚，同时会直接结束方法调用）
                    throw new SkuWareException();
                }

            }
        }

    }

    @Override
    public OrderEntity getOrderEntityByOrderSn(String orderSn) {
        OrderEntity orderEntity = this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        return orderEntity;
    }

    @Override
    // 关闭订单
    public void closeOrderHandle(OrderEntity orderEntity) {
        // 根据MQ获取到的订单信息，再去DB中根据orderId作查询，判断是否为空或状态是刚创建待付款的（避免订单之前被回滚了的问题）
        OrderEntity byId = this.getById(orderEntity.getId());
        if(byId != null && byId.getStatus() == OrderStatusEnum.CREATE_NEW.getCode()){
            OrderEntity entity = new OrderEntity();
            entity.setId(orderEntity.getId());
            entity.setStatus(OrderStatusEnum.CANCLED.getCode());// 已取消
            this.updateById(entity);
            // 主动将要关单的订单信息OrderEntity发送给库存服务的延时队列
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(entity,orderTo);
            rabbitTemplate.convertAndSend("order-event-exchange","order.release.other",orderTo);
        }
    }

    @Override
    // 构建支付用的表单信息vo——PayVo
    public PayVo orderPay(String orderSn) {
        PayVo payVo = new PayVo();
        // 根据orderSn查出OrderEntity和关联的List<OrderItems>
        OrderEntity orderEntity = this.getOrderEntityByOrderSn(orderSn);
        BigDecimal payAmount = new BigDecimal(orderEntity.getPayAmount().toString()).setScale(2, RoundingMode.UP);
        List<OrderItemEntity> itemEntities = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));
        OrderItemEntity orderItemEntity = itemEntities.get(0);

        payVo.setOut_trade_no(orderSn);// 订单号
        payVo.setTotal_amount(payAmount.toString());// 应付总额
        payVo.setSubject(orderItemEntity.getSkuName());// 订单名称（这里使用商品名称）
        payVo.setBody(orderItemEntity.getSkuAttrsVals());// 订单描述（这里使用商品属性描述）
        return payVo;
    }

    @Override
    // 获取指定memberId所关联的所有订单/订单项（分页查询）
    public PageUtils getOrdersByMemberIdWithParams(Map<String, Object> params) {
        MemberUserInfoVo memberUserInfoVo = OrderLoginInterceptor.threadLocal.get();
        Long userId = memberUserInfoVo.getId();

        IPage<OrderEntity> page = this.page(
            // 添加指定的查询条件后的分页查询（按OrderEntity的id降序排列）
            new Query<OrderEntity>().getPage(params),
            new QueryWrapper<OrderEntity>().eq("member_id", userId).orderByDesc("id")
        );

        // 设置分页查询的记录体
        List<OrderEntity> orders = page.getRecords();
        if(orders != null && orders.size() > 0){
            orders = orders.stream().map(item -> {
                String orderSn = item.getOrderSn();
                List<OrderItemEntity> orderItems = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));
                item.setItems(orderItems);

                return item;
            }).collect(Collectors.toList());
        }

        page.setRecords(orders);
        return new PageUtils(page);
    }

    @Override
    // 根据支付宝异步通知得到的封装PayAsyncVo，去保存交易流水，并修改订单的状态
    public void handlePayResult(PayAsyncVo payAsyncVo) {
        // 保存交易流水
        PaymentInfoEntity paymentInfoEntity = new PaymentInfoEntity();
        paymentInfoEntity.setAlipayTradeNo(payAsyncVo.getTrade_no());
        paymentInfoEntity.setOrderSn(payAsyncVo.getOut_trade_no());
        paymentInfoEntity.setPaymentStatus(payAsyncVo.getTrade_status());
        paymentInfoEntity.setCallbackTime(payAsyncVo.getNotify_time());
        paymentInfoEntity.setCreateTime(new Date());

        paymentInfoService.save(paymentInfoEntity);
        // 修改订单的状态
        if("TRADE_SUCCESS".equals(payAsyncVo.getTrade_status()) || "TRADE_FINISHED".equals(payAsyncVo.getTrade_status())){
            Integer status = OrderStatusEnum.PAYED.getCode();
            Date modifyTime = new Date();
            String orderSn = payAsyncVo.getOut_trade_no();
            this.baseMapper.updateOrderStatus(status,modifyTime,orderSn);
        }

    }

    @Override
    // 构建/保存秒杀单
    public void buildSeckillOrder(SeckillOrderTo seckillOrderTo) {
        // 保存订单信息
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(seckillOrderTo.getOrderSn());
        orderEntity.setMemberId(seckillOrderTo.getMemberId());
        BigDecimal payAmount = seckillOrderTo.getSeckillPrice().multiply(new BigDecimal("" + seckillOrderTo.getNum()));
        orderEntity.setPayAmount(payAmount);
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        this.save(orderEntity);
        // 保存订单项信息（秒杀单只有1个订单项）
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        orderItemEntity.setOrderSn(seckillOrderTo.getOrderSn());
        orderItemEntity.setRealAmount(payAmount);
        orderItemEntity.setSkuQuantity(seckillOrderTo.getNum());
        orderItemEntity.setSkuId(seckillOrderTo.getSkuId());
        // TODO 设置当前skuId对应的商品详细信息
        orderItemService.save(orderItemEntity);

    }

    // 保存订单相关数据（订单 + 订单项）
    private void saveOrderInfo(OrderCreateTo orderCreateTo) {
        OrderEntity orderEntity = orderCreateTo.getOrder();
        orderEntity.setCreateTime(new Date());
        this.save(orderEntity);

        List<OrderItemEntity> orderItems = orderCreateTo.getOrItems();
        orderItemService.saveBatch(orderItems);
    }

    // 2、构建订单to，包含订单数据、订单项数据、计算价格相关
    private OrderCreateTo createOrderTo(OrderSubmitVo orderSubmitVo) {
        OrderCreateTo orderCreateTo = new OrderCreateTo();
        // 生成1个唯一订单号
        String orderSn = IdWorker.getTimeId();
        // 1、构造订单数据
        OrderEntity orderEntity = buildOrderInfo(orderSn,orderSubmitVo);
        // 2、构造订单项数据
        List<OrderItemEntity> itemEntities = buildOrderItemsInfo(orderSn);
        orderCreateTo.setOrItems(itemEntities);
        // 3、计算价格相关
        computePrice(orderEntity,itemEntities);// 引用数据类型的传递机制（设置价格相关项）
        orderCreateTo.setOrder(orderEntity);


        return orderCreateTo;
    }
    // 构造订单数据
    private OrderEntity buildOrderInfo(String orderSn, OrderSubmitVo orderSubmitVo) {
        MemberUserInfoVo memberUserInfoVo = OrderLoginInterceptor.threadLocal.get();

        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(orderSn);
        orderEntity.setMemberId(memberUserInfoVo.getId());
        orderEntity.setMemberUsername(memberUserInfoVo.getUsername());

        // 远程调用Ware服务，获取到指定addrId对应的FareVo（设置运费信息和收货人/地址信息）
        R r = wareFeignService.getFareByAddrId(orderSubmitVo.getAddrId());
        if(r != null && r.getCode() == 0){
            FareReceiveVo fareReceiveVo = r.getData("frData", new TypeReference<FareReceiveVo>() {
            });
            if(fareReceiveVo != null){
                MemberAddressVo address = fareReceiveVo.getAddress();
                orderEntity.setFreightAmount(fareReceiveVo.getFare());
                orderEntity.setReceiverProvince(address.getProvince());
                orderEntity.setReceiverCity(address.getCity());
                orderEntity.setReceiverRegion(address.getRegion());
                orderEntity.setReceiverDetailAddress(address.getDetailAddress());
                orderEntity.setReceiverName(address.getName());
                orderEntity.setReceiverPhone(address.getPhone());
                orderEntity.setReceiverPostCode(address.getPostCode());
            }
        }
        // 设置订单的状态
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());// 待付款
        orderEntity.setAutoConfirmDay(7);// 自动确认时间（天）
        orderEntity.setConfirmStatus(0);// 确认收货状态[0->未确认；1->已确认]

        return orderEntity;
    }

    // 价格计算（商品实付总价、商品应付总价、优惠信息、赠送信息等）
    private void computePrice(OrderEntity orderEntity,List<OrderItemEntity> orderItemEntities) {
        BigDecimal totalAmount = new BigDecimal("0.0");
        BigDecimal payAmount = new BigDecimal("0.0");
        BigDecimal promotionAmount = new BigDecimal("0.0");
        BigDecimal couponAmount = new BigDecimal("0.0");
        BigDecimal integrationAmount = new BigDecimal("0.0");
        Integer giftedIntegrationAccount = 0;
        Integer giftedGrowthPointAccount = 0;

        for (OrderItemEntity itemEntity : orderItemEntities) {
            totalAmount = totalAmount.add(itemEntity.getRealAmount());
            promotionAmount = promotionAmount.add(itemEntity.getPromotionAmount());
            couponAmount = couponAmount.add(itemEntity.getCouponAmount());
            integrationAmount = integrationAmount.add(itemEntity.getIntegrationAmount());
            giftedGrowthPointAccount += itemEntity.getGiftGrowth();
            giftedIntegrationAccount += itemEntity.getGiftIntegration();
        }
        payAmount = payAmount.add(totalAmount).subtract(orderEntity.getFreightAmount());
        orderEntity.setTotalAmount(totalAmount);
        orderEntity.setPayAmount(payAmount);
        orderEntity.setPromotionAmount(promotionAmount);
        orderEntity.setCouponAmount(couponAmount);
        orderEntity.setGrowth(giftedGrowthPointAccount);
        orderEntity.setIntegration(giftedIntegrationAccount);

        //设置删除状态(0-未删除，1-已删除)
        orderEntity.setDeleteStatus(0);

    }

    // 构造所有订单项数据
    private List<OrderItemEntity> buildOrderItemsInfo(String orderSn) {
        List<OrderItemEntity> orderItems = null;
        // 远程调用cart服务，获取到要购买的购物项列表
        List<OrderItemVo> userCartItems = cartFeignService.getUserCartItems();
        if(userCartItems != null && userCartItems.size() > 0){
            orderItems = userCartItems.stream().map(item -> {
                // 根据每个要购买的购物项，构建对应的OrderItemEntity
                OrderItemEntity orderItemEntity = buildOrderItem(item);
                // 设置统一的订单编号，对应的是一张订单（包含多个订单项）
                orderItemEntity.setOrderSn(orderSn);
                return orderItemEntity;
            }).collect(Collectors.toList());
        }
        return orderItems;
    }
    // 构建每个订单项数据
    private OrderItemEntity buildOrderItem(OrderItemVo item) {
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        // 1、设置商品的SPU信息（根据skuId，远程调用product服务，查询所关联的spuId对应的SpuInfoEntity）
        R r = productFeignService.getSpuInfoBySkuId(item.getSkuId());
        if(r != null && r.getCode() == 0){
            SpuInfoVo spuInfoVo = r.getData("data", new TypeReference<SpuInfoVo>() {
            });
            if(spuInfoVo != null){
                orderItemEntity.setSpuId(spuInfoVo.getId());
                orderItemEntity.setSpuName(spuInfoVo.getSpuName());
                orderItemEntity.setSpuBrand(spuInfoVo.getBrandName());
                orderItemEntity.setCategoryId(spuInfoVo.getCatalogId());
            }
        }

        // 2、设置商品的SKU信息
        orderItemEntity.setSkuId(item.getSkuId());
        orderItemEntity.setSkuName(item.getTitle());
        orderItemEntity.setSkuPic(item.getImage());
        orderItemEntity.setSkuPrice(item.getPrice());
        orderItemEntity.setSkuQuantity(item.getCount());
        // 3、设置商品的销售属性信息：将list集合中每个元素取出并以分号连接成String字符串
        String skuAttrValues = StringUtils.collectionToDelimitedString(item.getSkuAttrValues(), ";");
        orderItemEntity.setSkuAttrsVals(skuAttrValues);
        // 4、TODO 各类优惠信息（商品促销减免、优惠券、积分优惠等）假定为0
        orderItemEntity.setPromotionAmount(BigDecimal.ZERO);
        orderItemEntity.setCouponAmount(BigDecimal.ZERO);
        orderItemEntity.setIntegrationAmount(BigDecimal.ZERO);

        // 5、成长值和积分信息
        orderItemEntity.setGiftGrowth(item.getPrice().multiply(new BigDecimal(item.getCount()+"")).intValue());// 赠送的成长值
        orderItemEntity.setGiftIntegration(item.getPrice().multiply(new BigDecimal(item.getCount()+"")).intValue());// 赠送的积分

        // 6、设置每类购物项商品的实际支付总价（减去各类优惠价格）
        BigDecimal originAmount = item.getPrice().multiply(new BigDecimal(item.getCount()+""));
        BigDecimal realAmount = originAmount.subtract(orderItemEntity.getPromotionAmount())
                .subtract(orderItemEntity.getCouponAmount())
                .subtract(orderItemEntity.getIntegrationAmount());
        orderItemEntity.setRealAmount(realAmount);

        return orderItemEntity;
    }


}